package com.xteam.xnetworkdisk.sys.service.impl;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xteam.xnetworkdisk.common.CurrentUser;
import com.xteam.xnetworkdisk.constant.Constant;
import com.xteam.xnetworkdisk.constant.FIleTypeConstant;
import com.xteam.xnetworkdisk.dto.ChunksReturnStatusDTO;
import com.xteam.xnetworkdisk.dto.FilePageByTypeDTO;
import com.xteam.xnetworkdisk.dto.FilePageDTO;
import com.xteam.xnetworkdisk.dto.FileRenameDTO;
import com.xteam.xnetworkdisk.exception.CommonException;
import com.xteam.xnetworkdisk.sys.dao.ContactsDao;
import com.xteam.xnetworkdisk.sys.dao.SettingsDao;
import com.xteam.xnetworkdisk.sys.dao.UserDao;
import com.xteam.xnetworkdisk.sys.entity.ContactsEntity;
import com.xteam.xnetworkdisk.sys.entity.SettingsEntity;
import com.xteam.xnetworkdisk.sys.entity.UserEntity;
import com.xteam.xnetworkdisk.utils.*;

import com.xteam.xnetworkdisk.vo.*;
import com.xteam.xnetworkdisk.websocket.ProgressWebSocketHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.stereotype.Service;


import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.xteam.xnetworkdisk.sys.dao.FileDao;
import com.xteam.xnetworkdisk.sys.entity.FileEntity;
import com.xteam.xnetworkdisk.sys.service.FileService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;

@Slf4j
@Service("fileService")
public class FileServiceImpl extends ServiceImpl<FileDao, FileEntity> implements FileService {

    @Autowired
    private ChunkFileLocalUtils chunkFileUtils;

    @Autowired
    private ContactsDao contactsDao;

    @Autowired
    private ProgressWebSocketHandler progressWebSocketHandler;

    @Autowired
    private FileDao fileDao;

//    @Autowired
//    private MinioUtilsNew minioUtils;

    @Autowired
    private FileLocalUtils fileLocalUtils;

    @Autowired
    private RedisUtils redisUtils;


    @Autowired
    private EmailUtil emailUtil;

    @Autowired
    private UserDao userDao;
    @Autowired
    private SettingsDao settingsDao;

    @Override
    public R filePageQuery(FilePageDTO filePageDTO) {
        LambdaQueryWrapper<FileEntity> wrapper = new LambdaQueryWrapper<>();
        if(CurrentUser.getCurrentUserId()==null){
            throw new CommonException(Constant.USER_NOT_AUTHENTICATED);
        }
        wrapper.eq(FileEntity::getUserId,CurrentUser.getCurrentUserId());
        // 设置父目录条件
        if(filePageDTO.getFolderId()!=null){
            wrapper.eq(FileEntity::getFileId,filePageDTO.getFolderId());
        }else if(filePageDTO.getIsValid()){
            wrapper.isNull(FileEntity::getFolderId);
        }
        List<FileEntity> fileEntities = fileDao.selectList(wrapper);

        if(fileEntities.isEmpty()){
            return R.error("用户初始目录不存在，请联系管理员进行处理");
        }

        if(ArrayUtil.isEmpty(fileEntities)){
            return R.error();
        }

        if(fileEntities.isEmpty()){
            return R.error("用户初始目录不存在，请联系管理员进行处理");
        }

        FileEntity fileEntity = fileEntities.get(0);
        if(fileEntity == null){
            return R.error("用户初始目录出现错误，请联系管理员进行处理");
        }
        Integer PfileId = fileEntity.getFileId();

        Page<FileEntity> file = new Page<>(filePageDTO.getPageIndex(), filePageDTO.getPageSize());

        // 设置排序规则，默认降序
        if(StrUtil.isNotBlank(filePageDTO.getSidx())){
            if(StrUtil.isNotBlank(filePageDTO.getOrder())){
                if(filePageDTO.getOrder().equals(Constant.ASC)){
                    file.addOrder(OrderItem.asc(filePageDTO.getSidx()));
                }
            }else {
                file.addOrder(OrderItem.desc(filePageDTO.getSidx()));
            }
        }

        Integer pFileId = fileEntity.getFileId();

        LambdaQueryWrapper<FileEntity> fileEntityQueryWrapper = new LambdaQueryWrapper<>();
        fileEntityQueryWrapper.eq(FileEntity::getFolderId,pFileId);

        fileEntityQueryWrapper.eq(FileEntity::getIsValid,filePageDTO.getIsValid());
        if(filePageDTO.getIsFolder()!=null){
            fileEntityQueryWrapper.eq(FileEntity::getIsFolder,filePageDTO.getIsFolder());
        }

        // 设置文件名条件
        if(StrUtil.isNotBlank(filePageDTO.getFileName())){
            fileEntityQueryWrapper.like(FileEntity::getFileName,filePageDTO.getFileName());
        }

        if(filePageDTO.getFileType() != null) {
            fileEntityQueryWrapper.eq(FileEntity::getFileType,filePageDTO.getFileType());
        }

        // 设置日期条件
        if(filePageDTO.getUpdatedAt()!=null){
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(filePageDTO.getUpdatedAt());
            calendar.add(Calendar.DAY_OF_MONTH,1);
            Date over = calendar.getTime();
            fileEntityQueryWrapper.between(FileEntity::getUpdatedAt,filePageDTO.getUpdatedAt(),over);
        }

        Page<FileEntity> fileEntityPage = fileDao.selectPage(file, fileEntityQueryWrapper);

        return R.ok().put("files",fileEntityPage).put("pFileId",PfileId);

    }

    @Override
    public void rename(FileRenameDTO fileRenameDTO) {
        FileEntity fileEntity = fileDao.selectById(fileRenameDTO.getFileId());
        String newName = fileRenameDTO.getNewName();
        Integer folderId = fileRenameDTO.getFolderId();
        if(!CurrentUser.getCurrentUserId().equals(fileEntity.getUserId())){
            throw new CommonException(Constant.USER_NOT_AUTHENTICATED);
        }

        //判断是否重名，如果重名更新文件名
        if (isFileNameExists(newName,true,folderId,fileEntity.getFileId())){
            long l = System.currentTimeMillis();
            String[] strings = Tools.splitFileName(newName);
            newName = strings[0] + "_" + l + "." + strings[1];
        }

        fileEntity.setFileName(newName);
        fileEntity.setUpdatedAt(new Date());
        fileDao.updateById(fileEntity);
    }

    @Override
    public R upload(MultipartFile file, String md5Value, Integer folderId) {
//        UserEntity userEntity = userDao.selectById(CurrentUser.getCurrentUserId());
//        Long totalSpace = userEntity.getTotalSpace();
//        Long useSpace = userEntity.getUseSpace();
//
//        //获取文件相应的信息
//        String fileName = file.getOriginalFilename();
//        Long fileSize = file.getSize();
//        String contentType = file.getContentType();
//        String bucketName = Constant.USER_FILE_BUKET;
//
//        if(fileSize + useSpace > totalSpace){
//            throw new CommonException(Constant.USER_SPACE_NOT_ENOUGH);
//        }
//
//        //通过contentType得到它在数据库中的类型所代表的id
//        int fileType = FileTypeUtils.getFileType(contentType);
//
//        try {
//            String suffix = Tools.getSuffix(fileName);
//            //上传文件到minio
//            String videoFileName = minioUtils.uploadFile(file, bucketName,suffix);
//
//            //在数据库生成相应的信息
//            FileEntity fileEntity = new FileEntity();
//            fileEntity.setFileName(fileName);
//            fileEntity.setFilePath(endpoint + "/" + bucketName + "/" + videoFileName);
//            fileEntity.setFileSize(fileSize);
//            fileEntity.setCreatedAt(new Date());
//            fileEntity.setIsFolder(false);
//            fileEntity.setIsValid(true);
//            fileEntity.setFileKey(md5Value);
//            fileEntity.setFileType(fileType);
//            fileEntity.setFolderId(folderId);
//            fileEntity.setUserId(CurrentUser.getCurrentUserId());
//            //fileEntity.setUserId(1785409564);
//
//            //新增file记录
//            fileDao.insertFile(fileEntity);
//            userEntity.setUseSpace(useSpace + fileSize);
//            userDao.updateById(userEntity);
//
//            //类型是视频类型
//            if (fileType == FIleTypeConstant.VIDEO) {
//
//                try (OutputStream thumbnailStream = new ByteArrayOutputStream()) {
//                    // 生成封面图像
//                    VideoUtil.getVideoImage(file, thumbnailStream);
//
//                    // 上传封面图像到 MinIO
//                    String thumbnailFileName = videoFileName + Constant.VIDEO_SUFFIX;
//                    InputStream thumbnailInputStream = new ByteArrayInputStream(((ByteArrayOutputStream) thumbnailStream).toByteArray());
//                    minioUtils.uploadFile(thumbnailInputStream, bucketName, thumbnailFileName,Constant.VIDEO_INDEX_IMAGE_TPYE);
//                }
//            }
//        } catch (Exception e) {
//            throw new RuntimeException(e);
//        }
        return R.ok();
    }

    @Override
    public R deleteFile(List<Integer> fileId) {
        for (Integer id : fileId) {
            FileEntity fileEntity = fileDao.selectById(id);
            if (fileEntity == null) {
                return R.error(Constant.FILE_NOT_FOUND);
            }
            deleteFileRecursive(fileEntity);
        }
        return R.ok("文件删除成功");
    }

    @Override
    @Transactional
    public R deleteFileTry(List<Integer> fileId) {
        for (Integer id : fileId) {
            FileEntity fileEntity = fileDao.selectById(id);
            if (fileEntity == null) {
                return R.error(Constant.FILE_NOT_FOUND);
            }
            deleteFileRecursiveTry(fileEntity);
        }
        return R.ok("文件移动到回收站");
    }

    @Override
    public R getDir(Integer fileId, Integer userid) {
        LambdaQueryWrapper<FileEntity> wrapper = new LambdaQueryWrapper<>();

        wrapper.eq(FileEntity::getUserId,userid);

        if (fileId == 0){
            wrapper.isNull(FileEntity::getFolderId);
            List<FileEntity> fileEntities = fileDao.selectList(wrapper);
            FileEntity fileEntity = fileEntities.get(0);
            if(Objects.isNull(fileEntity)){
                return R.error("未找到主目录");
            }
            fileId = fileEntity.getFileId();
        }

        LambdaQueryWrapper<FileEntity> newwrapper = new LambdaQueryWrapper<>();

        newwrapper.eq(FileEntity::getFolderId,fileId);
        newwrapper.eq(FileEntity::getIsFolder,true);
        newwrapper.eq(FileEntity::getIsValid,true);

        List<FileEntity> fileEntities = fileDao.selectList(newwrapper);

        return R.ok().put("files",fileEntities);
    }

    @Override
    public R listPageByType(FilePageByTypeDTO filePageByTypeDTO, Integer userid) {
        LambdaQueryWrapper<FileEntity> wrapper = new LambdaQueryWrapper<>();
        if(CurrentUser.getCurrentUserId()==null){
            throw new CommonException(Constant.USER_NOT_AUTHENTICATED);
        }
        wrapper.eq(FileEntity::getUserId,CurrentUser.getCurrentUserId());

        if(filePageByTypeDTO.getFileType() != null) {
            wrapper.eq(FileEntity::getFileType,filePageByTypeDTO.getFileType());
        }

        // 设置文件名条件
        if(StrUtil.isNotBlank(filePageByTypeDTO.getFileName())){
            wrapper.like(FileEntity::getFileName,filePageByTypeDTO.getFileName());
        }


        wrapper.eq(FileEntity::getIsFolder,false);
        wrapper.eq(FileEntity::getIsValid,true);
        wrapper.ne(FileEntity::getFolderId,Constant.TEMP_FOLDER);

        List<FileEntity> fileEntities = fileDao.selectList(wrapper);


        return R.ok().put("files",fileEntities);
    }

    @Override
    public R moveFile(MoveFileVO moveFileVO) {

        ArrayList<Integer> fileIds = moveFileVO.getFileIds();
        Integer targetFolderId = moveFileVO.getTargetFolderId();

        for (int i = 0; i < fileIds.size(); i++) {
            FileEntity fileEntity = fileDao.selectById(fileIds.get(i));
            String fileName = fileEntity.getFileName();

            if (fileEntity == null){
                return R.error("不存在文件");
            }

            //判断是否重名，如果重名更新文件名
            if (isFileNameExists(fileName,true,targetFolderId, fileEntity.getFileId())){
                long l = System.currentTimeMillis();
                String[] strings = Tools.splitFileName(fileName);
                fileName = strings[0] + "_" + l + "." + strings[1];
            }

            fileEntity.setFileName(fileName);
            fileEntity.setFolderId(targetFolderId);
            fileEntity.setUpdatedAt(new Date());
            fileDao.updateById(fileEntity);
        }

        return R.ok();
    }

    @Override
    public R createFolder(CreateFolderVO createFolderVO, Integer userid) {

        String folderName = createFolderVO.getFolderName();
        Integer targetFolderId = createFolderVO.getTargetFolderId();

        //判断是否重名，如果重名更新文件名
        if (isFileNameExists(folderName,true,targetFolderId, null)){
            long l = System.currentTimeMillis();
            String[] strings = Tools.splitFileName(folderName);
            folderName = strings[0] + "_" + l + "." + strings[1];
        }


        FileEntity fileEntity = fileDao.selectById(targetFolderId);
        if(Objects.isNull(fileEntity)){
            return R.ok("所选目录不存在");
        }

        FileEntity fileEntity1 = new FileEntity();
        fileEntity1.setFolderId(targetFolderId);
        fileEntity1.setFileName(folderName);
        fileEntity1.setIsFolder(true);
        fileEntity1.setIsValid(true);
        fileEntity1.setCreatedAt(new Date());
        fileEntity1.setUpdatedAt(new Date());
        fileEntity1.setUserId(userid);
        fileDao.insertFile(fileEntity1);
        return R.ok();
    }

    @Override
    public R createDownloadLink(String fileId) {
//        FileEntity fileEntity = fileDao.selectById(fileId);
//        String filePath = fileEntity.getFilePath();
//        String fileName = fileEntity.getFileName();
//        String s = null;
//        try {
//            s = minioUtils.generatePresignedUrl(filePath,fileName);
//        } catch (Exception e) {
//            throw new RuntimeException(e);
//        }
//        return R.ok().put("url",s);
        return R.ok();
    }

    @Override
    public R sendFileByEmail(SendFileByEmailVO sendFileByEmailVO) {
        Integer contactsId = sendFileByEmailVO.getContactsId();
        Integer fileId = sendFileByEmailVO.getFileId();

        Integer currentUserId = CurrentUser.getCurrentUserId();
        FileEntity fileEntity = fileDao.selectById(fileId);

        Boolean isFolder = fileEntity.getIsFolder();
        if(isFolder) {
            return R.error("文件夹不能发送");
        }

        UserEntity userEntity = userDao.selectById(currentUserId);
        String email = userEntity.getUsername();
        String fileName = fileEntity.getFileName();
        Integer fileType = fileEntity.getFileType();
        String filePath = fileEntity.getFilePath();
        String[] strings = Tools.extractFilename(filePath);
        String filenameR = strings[0] + strings[1];
        String fullPath = fileLocalUtils.getFullPath(filenameR);

        System.out.println(fullPath);

        if(contactsId != null){
            ContactsEntity contactsEntity = contactsDao.selectById(contactsId);
            email = contactsEntity.getEmail();
        }

        InputStream fileInputStreamFromDisplayPath = fileLocalUtils.getFileInputStreamFromDisplayPath(fullPath);

        try {
            emailUtil.sendEmail(email, "骁合云盘传输：" + fileName, fileInputStreamFromDisplayPath, fileType, fileName);
            return R.ok("Email sent successfully!");
        } catch (Exception e) {
            e.printStackTrace();
            return R.error("Failed to send email: " + e.getMessage());
        }
    }

    @Override
    public R instantUpload(InstantUploadVO instantUploadVO){
        String md5Value = instantUploadVO.getMd5Value();
        Integer folderId = instantUploadVO.getFolderId();
        String fileName = instantUploadVO.getFileName();

        //判断是否重名，如果重名更新文件名
        if (isFileNameExists(fileName,true,folderId, null)){
            long l = System.currentTimeMillis();
            String[] strings = Tools.splitFileName(fileName);
            fileName = strings[0] + "_" + l + "." + strings[1];
        }

        //找到对应的文件进行copy
        QueryWrapper<FileEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("file_key",md5Value);
        wrapper.eq("is_valid",true);
        List<FileEntity> fileEntities = fileDao.selectList(wrapper);
        FileEntity fileEntity = fileEntities.get(0);
        String filePath = fileEntity.getFilePath();
        String[] strings = Tools.extractFilename(filePath);
        String fileName1 = strings[0] + strings[1];

        Integer currentUserId = CurrentUser.getCurrentUserId();
        UserEntity userEntity = userDao.selectById(currentUserId);

        String newFileName = fileLocalUtils.copyFile(Constant.USER_FILE_BUKET, fileName1);
        //在数据库生成相应的信息
        FileEntity fileEntity1 = new FileEntity();
        fileEntity1.setFileName(fileName);
        fileEntity1.setFilePath(Constant.FILE_LOCAL_PATH + "/" + Constant.USER_FILE_BUKET + "/" + newFileName);
        fileEntity1.setFileSize(fileEntity.getFileSize());
        fileEntity1.setCreatedAt(new Date());
        fileEntity1.setIsFolder(false);
        fileEntity1.setIsValid(true);
        fileEntity1.setFileKey(md5Value);
        fileEntity1.setFileType(fileEntity.getFileType());
        fileEntity1.setFolderId(folderId);
        fileEntity1.setUpdatedAt(new Date());
        fileEntity1.setUserId(currentUserId);
        //新增file记录
        fileDao.insertFile(fileEntity1);

        //更新用量
        userEntity.setUseSpace(userEntity.getUseSpace() + fileEntity.getFileSize());
        userDao.updateById(userEntity);

        String suffix = Tools.getSuffix(fileName);
        int fileType = FileTypeUtils.getFileTypeByExtension(suffix.substring(1));

        //如果是视频，生成对应的预览视频
        ExecutorService executorService = ThreadPoolManager.getExecutorService();
        if(fileType == FIleTypeConstant.VIDEO){
            executorService.submit(()->{
                String redisKey = "file:upload:m3u8_e:" + md5Value;
                redisUtils.lock(redisKey);
                System.out.println("加锁");
                System.out.println("生成TS");
                // 生成TS，此处的生成可以直接copy先前文件的
                String[] strings1 = Tools.splitFileName(newFileName);
                fileLocalUtils.copyFile(Constant.USER_TS_BUKET, strings[0] + "_e.m3u8",strings1[0] + "_e.m3u8");
                redisUtils.unlock(redisKey);
                System.out.println("解锁");
            });
        }

        return R.ok();
    }

    Boolean isFileNameExists(String fileName, Boolean isValid, Integer folderId, Integer fileId){
        Integer currentUserId = CurrentUser.getCurrentUserId();
        QueryWrapper<FileEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("file_name",fileName);
        wrapper.eq("user_id",currentUserId);
        wrapper.eq("is_valid",isValid);
        wrapper.eq("folder_id",folderId);
        if(fileId != null){
            wrapper.ne("file_id",fileId);
        }
        Long count = fileDao.selectCount(wrapper);
        if (count > 0){
            return true;
        }
        return false;
    }
    Boolean isFileNameExists(String fileName,Boolean isValid){
        Integer currentUserId = CurrentUser.getCurrentUserId();
        QueryWrapper<FileEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("file_name",fileName);
        wrapper.eq("user_id",currentUserId);
        wrapper.eq("is_valid",isValid);
        Long count = fileDao.selectCount(wrapper);
        if (count > 0){
            return true;
        }
        return false;
    }


    @Override
    public R uploadChunk(MultipartFile file, String md5Value, Integer folderId, Integer index, Integer chunks, String fileName, Long fileSize, Integer userid) {

        //准备好返回对象
        ChunksReturnStatusDTO chunksReturnStatusDTO = new ChunksReturnStatusDTO();
        chunksReturnStatusDTO.setMd5Value(md5Value);
        chunksReturnStatusDTO.setIndex(index);
        chunksReturnStatusDTO.setChunks(chunks);

        String suffix = Tools.getSuffix(fileName);
        int fileType = FileTypeUtils.getFileTypeByExtension(suffix.substring(1));

        //如果是视频且是第一个分片，生成视频的封面并存入redis中
//
//        if (index == 1 && FIleTypeConstant.VIDEO == fileType){
//            try (OutputStream thumbnailStream = new ByteArrayOutputStream()) {
//                // 生成封面图像
//                log.info("生成封面图像");
//                VideoUtil.getVideoImage(file, thumbnailStream);
//                chunkFileUtils.write("file:upload:" + md5Value + ":" + Constant.VIDEO_THUMBNAIL,thumbnailStream);
//                log.info("生成封面图像并存储");
//            } catch (IOException e) {
//                throw new RuntimeException(e);
//            }
//        }

        //如果只有一个分片，则直接上传
        if (chunks == 1){
            log.info("md5:" + md5Value);
            log.info("只有一个分片");
            R upload = upload(file, md5Value, folderId,fileName,fileSize,fileType,userid);
            updateUploadProgress(md5Value, userid, chunks);
            log.info("一个分片上传成功");
            return upload.put("chunkStatus", chunksReturnStatusDTO);
        }

        //如果有多个分片，将分片上传到Redis中
        String redisKey = "file:upload:" + md5Value + ":" + index;
        try {
            chunkFileUtils.write(redisKey,file); // 将文件的字节数组存入Redis
        } catch (IOException e) {
            e.printStackTrace();
            return R.error("文件上传失败");
        }

        //将分片加入到集合当中
        log.info("分片加入集合");
        markChunkAsUploaded(md5Value, index,chunks,userid);
        // 判断是否所有分片都上传完毕，若上传完毕则合并分片
        if (checkAllChunksUploaded(md5Value, chunks)) {
            mergeAndUploadFile(md5Value, chunks, fileName, folderId, fileSize,fileType,userid);
            return R.ok().put("chunkStatus", chunksReturnStatusDTO);
        }

        log.info("不是最后一个分片");
        return R.ok().put("chunkStatus", chunksReturnStatusDTO);
    }


    public R mergeAndUploadFile(String md5Value, int chunks, String fileName, Integer folderId, long fileSize, Integer fileType, Integer userId) {

        String redisKey = "file:upload:end:" + md5Value;
        Boolean lock = redisUtils.acquireLock(redisKey,5 * 60 * 1000);
        if(!lock) {
            return R.error("该文件正在被合并");
        }

        try {
            // 合并分片
            log.info("合并分片");
            try (InputStream inputStream = mergeFileChunks(md5Value, chunks)) {
                // 文件上传
                System.out.println(inputStream == null);

                //判断是否重名，如果重名更新文件名
                if (isFileNameExists(fileName,true,folderId, null)){
                    long l = System.currentTimeMillis();
                    String[] strings = Tools.splitFileName(fileName);
                    fileName = strings[0] + "_" + l + "." + strings[1];
                }
                upload(inputStream, md5Value, folderId, fileName, fileSize, fileType,userId);
            } // inputStream 在此处会被自动关闭

            ExecutorService executorService = ThreadPoolManager.getExecutorService();

            executorService.submit(() -> {
                // 清理文件上传相关的缓存
                log.info("清理与文件上传相关的缓存");
                chunkFileUtils.clearFileUploadCache(md5Value, chunks);

                // 清理与已上传分片相关的集合
                log.info("清理与已上传分片相关的集合");
                String redisKeySet = "file:upload:chunks:" + md5Value;
                redisUtils.delete(redisKeySet);
                redisUtils.releaseLock(redisKey);
            });

        } catch (IOException e) {
            e.printStackTrace();
            return R.error("文件合并失败");
        }
        return R.ok();
    }

    @Override
    @Transactional
    public R recover(List<Integer> fileIds) {

        for (int i = 0; i < fileIds.size(); i++) {
            FileEntity fileEntity = fileDao.selectById(fileIds.get(i));
            String fileName = fileEntity.getFileName();
            Integer folderId = fileEntity.getFolderId();

            //判断是否重名，如果重名更新文件名
            if (isFileNameExists(fileName,true,folderId, fileEntity.getFileId())){
                long l = System.currentTimeMillis();
                String[] strings = Tools.splitFileName(fileName);
                fileName = strings[0] + "_" + l + "." + strings[1];
            }

            fileEntity.setUpdatedAt(new Date());
            fileEntity.setIsValid(true);
            fileEntity.setFileName(fileName);

            //更新用户使用空间
            UserEntity userEntity = userDao.selectById(fileEntity.getUserId());
            userEntity.setUseSpace(userEntity.getUseSpace()+fileEntity.getFileSize());
            userDao.updateById(userEntity);

            // 执行更新操作
            int i1 = fileDao.updateById(fileEntity);
            if (i1 == 0){
                return R.error("文件恢复出现异常");
            }
        }

        return R.ok();
    }

    @Override
    public R getRecycleBin(Integer userid) {
        QueryWrapper<FileEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",userid);
        wrapper.eq("is_valid",false);
        List<FileEntity> fileEntities = fileDao.selectList(wrapper);
        return R.ok().put("files", fileEntities);
    }

    @Override
    public R determineUploadStrategy(String md5Value) {
        R result = new R();

        Boolean lock = redisUtils.lock(md5Value);

        if(!lock){
            result.put("code", 500);
            result.put("msg", "文件正在清理临时数据中，请稍后再试");
            return result;
        }

        redisUtils.unlock(md5Value);

        FileEntity fileEntity = null;

        QueryWrapper<FileEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("file_key",md5Value);
        List<FileEntity> fileEntities = fileDao.selectList(wrapper);
        if(fileEntities != null && fileEntities.size() != 0){
            fileEntity = fileEntities.get(0);
        }
        System.out.println(fileEntities);

        Integer currentUserId = CurrentUser.getCurrentUserId();
        UserEntity userEntity = userDao.selectById(currentUserId);

        if(fileEntity != null && userEntity.getUseSpace() + fileEntity.getFileSize() > userEntity.getTotalSpace()){
            result.put("code", 500);
            result.put("msg", "空间不足，无法上传");
            return result;
        }

        if(fileEntity != null){
            result.put("code", Constant.DETERMINE_UPLOAD_STRATEGY_INSTANT);
            result.put("msg", "秒传");
            return result;
        }

        String redisKeySet = "file:upload:chunks:" + md5Value;
        SetOperations<String, Object> setOps = redisUtils.getSetOperations();
        Long size = setOps.size(redisKeySet);
        if(size != 0){
            // 获取已上传的分片集合
            Set<Object> uploadedChunks = setOps.members(redisKeySet);

            Set<String> uploadedChunkNumbers = uploadedChunks.stream()
                    .map(Object::toString) // 将 Object 转换为 String
                    .collect(Collectors.toSet());
            result.put("code",Constant.DETERMINE_UPLOAD_STRATEGY_RESUMABLE);
            result.put("msg","断点续传");
            result.put("uploadedChunkNumbers", uploadedChunkNumbers);
            return result;
        }

        result.put("code",Constant.DETERMINE_UPLOAD_STRATEGY_REGULAR);
        result.put("msg","一般上传");
        return result;
    }

    @Override
    public R canselUpload(CanselUploadVO canselUploadVO) {
        Integer chunks = canselUploadVO.getChunks();
        String md5Value = canselUploadVO.getMd5();

        Boolean lock = redisUtils.lock(md5Value);

        if(lock){
            // 清理文件上传相关的缓存
            log.info("清理与文件上传相关的缓存");
            chunkFileUtils.clearFileUploadCache(md5Value, chunks);

            // 清理与已上传分片相关的集合
            log.info("清理与已上传分片相关的集合");
            String redisKeySet = "file:upload:chunks:" + md5Value;
            redisUtils.delete(redisKeySet);

            redisUtils.unlock(md5Value);
        }
        return R.ok();
    }

    @Override
    public R judgePreview(String md5) {
        String redisKey = "file:upload:m3u8_e:" + md5;
        Boolean lock = redisUtils.lock(redisKey);
        System.out.println("尝试加锁："+lock);
        if(!lock){
            return R.error("文件正在生成预览视频中，请稍后再试");
        }
        redisUtils.unlock(redisKey);
        System.out.println("解锁");

        return R.ok();
    }

    public R upload(Object file, String md5Value, Integer folderId, String fileName, Long fileSize, Integer fileType,Integer userId) {

        //确定文件的类型
        MultipartFile multipartFile = null;
        InputStream inputStream = null;

        if (file instanceof MultipartFile) {
            multipartFile = (MultipartFile) file;
        } else if (file instanceof InputStream) {
            inputStream = (InputStream) file;
        }

        //获取用户余量
        UserEntity userEntity = userDao.selectById(userId);

        Long totalSpace = userEntity.getTotalSpace();
        Long useSpace = userEntity.getUseSpace();

        //计算是否还有余量
        String bucketName = Constant.USER_FILE_BUKET;
        if (fileSize + useSpace > totalSpace) {
            throw new CommonException(Constant.USER_SPACE_NOT_ENOUGH);
        }

        //通过后缀得到它在数据库中的类型所代表的id

        String suffix = Tools.getSuffix(fileName);

        //上传文件到minio
        try {
            System.out.println("上传文件");
            String videoFileName;
            if (multipartFile != null) {
                System.out.println("上传文件");
                videoFileName = fileLocalUtils.uploadFile(multipartFile, bucketName, suffix);
            } else {
                try (InputStream stream = inputStream) {
                    videoFileName = fileLocalUtils.uploadFile(stream, bucketName, suffix, fileSize, String.valueOf(fileType));
                }
            }

            System.out.println("数据库新增file");
            //在数据库生成相应的信息
            FileEntity fileEntity = new FileEntity();
            fileEntity.setFileName(fileName);
            fileEntity.setFilePath(Constant.FILE_LOCAL_PATH + "/" + bucketName + "/" + videoFileName);
            fileEntity.setFileSize(fileSize);
            fileEntity.setCreatedAt(new Date());
            fileEntity.setIsFolder(false);
            fileEntity.setIsValid(true);
            fileEntity.setFileKey(md5Value);
            fileEntity.setFileType(fileType);
            fileEntity.setFolderId(folderId);
            fileEntity.setUpdatedAt(new Date());
            System.out.println("CurrentUser.getCurrentUserId()" + CurrentUser.getCurrentUserId());
            fileEntity.setUserId(userId);
            //fileEntity.setUserId(1785409564);

            //新增file记录
            fileDao.insertFile(fileEntity);

            //更新用量
            userEntity.setUseSpace(useSpace + fileSize);
            userDao.updateById(userEntity);

            //类型是视频类型,上传封面
//            if (fileType == FIleTypeConstant.VIDEO) {
//                String thumbnailFileName = videoFileName + Constant.VIDEO_SUFFIX;
//                String redisKey = "file:upload:" + md5Value + ":" + Constant.VIDEO_THUMBNAIL;
//
//                byte[] bytes = chunkFileUtils.read(redisKey);
//
//                try (InputStream thumbnailInputStream = new ByteArrayInputStream(bytes)) {
//                    fileLocalUtils.uploadFile(thumbnailInputStream, bucketName, thumbnailFileName, Constant.VIDEO_INDEX_IMAGE_TPYE);
//                }
//            }
            ExecutorService executorService = ThreadPoolManager.getExecutorService();
            if(fileType == FIleTypeConstant.VIDEO){
                executorService.submit(()->{
                    String redisKey = "file:upload:m3u8_e:" + md5Value;
                    redisUtils.lock(redisKey);
                    System.out.println("加锁");
                    System.out.println("生成TS");
                    // 生成TS
                    fileLocalUtils.generateM3U8(videoFileName,md5Value);

                    redisUtils.unlock(redisKey);
                    System.out.println("解锁");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();

            throw new RuntimeException(e);
        }

        return R.ok();
    }


    public InputStream mergeFileChunks(String md5Value, int totalChunks) throws IOException {

        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

            // 按顺序读取每个分片并写入到输出流中
            for (int i = 1; i <= totalChunks; i++) {
                System.out.println(i);
                String redisKey = "file:upload:" + md5Value + ":" + i;

                byte[] chunkData = chunkFileUtils.read(redisKey);

                if (chunkData == null) {
                    throw new IOException("Failed to retrieve chunk " + i + " from Redis");
                }

                // 将分片数据写入输出流
                outputStream.write(chunkData);
            }

            // 将输出流转换为字节数组输入流
            byte[] mergedData = outputStream.toByteArray();
            return new ByteArrayInputStream(mergedData);

        } catch (Exception e) {
            e.printStackTrace();
            throw new IOException("Failed to merge file chunks", e);
        }
    }

    public void updateUploadProgress(String md5Value, Integer userid, int chunks){
        QueryWrapper<SettingsEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userid);
        SettingsEntity settingsEntity = settingsDao.selectOne(wrapper);

        if(settingsEntity.getSyncMode()!= Constant.UPLOAD_FILE_ASYNC) {
            return;
        }
        //并发过程中进度的显示

        Integer allIndex = progress(md5Value);

        try {
            int progress = (int) ((allIndex / (double) chunks) * 100);
            String progressMessage = String.valueOf(progress);
            progressWebSocketHandler.sendProgressUpdate(md5Value, progressMessage);

            // Check if upload is complete
            if (progress == 100) {
                // Notify completion
                progressWebSocketHandler.sendProgressUpdate(md5Value, "Upload complete");

                // Close the WebSocket connection
                progressWebSocketHandler.closeConnection(md5Value);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    public void markChunkAsUploaded(String md5Value, int index, Integer chunks, Integer userid) {
        String redisKeySet = "file:upload:chunks:" + md5Value;
        SetOperations<String, Object> setOps = redisUtils.getSetOperations();
        String idx = String.valueOf(index);
        setOps.add(redisKeySet, idx);

        // 更新上传进度
        updateUploadProgress(md5Value, userid, chunks);

    }

    public Integer progress(String md5Value) {

        String redisKeySet = "file:upload:chunks:" + md5Value;
        SetOperations<String, Object> setOps = redisUtils.getSetOperations();

        // 获取已上传的分片集合
        Set<Object> uploadedChunks = setOps.members(redisKeySet);

        int size = uploadedChunks.size();

        return size;
    }

    public Boolean checkAllChunksUploaded(String md5Value, int chunks) {
        String redisKeySet = "file:upload:chunks:" + md5Value;
        SetOperations<String, Object> setOps = redisUtils.getSetOperations();

        // 获取已上传的分片集合
        Set<Object> uploadedChunks = setOps.members(redisKeySet);
        System.out.println(uploadedChunks);
        System.out.println(chunks);
        if (uploadedChunks == null || uploadedChunks.size() != chunks) {
            return false; // 如果没有找到任何分片或分片数量不匹配，说明还有分片未上传
        }

        return true; // 所有分片都已上传
    }

    /**
     * 递归删除文件（假删除）
     * @param fileEntity
     */
    private void deleteFileRecursiveTry(FileEntity fileEntity) {
        // 删除当前文件或文件夹
        fileEntity.setIsValid(false);
        fileEntity.setUpdatedAt(new Date());
        String fileName = fileEntity.getFileName();

        //判断是否重名，如果重名更新文件名
        if (isFileNameExists(fileName,false)){
            long l = System.currentTimeMillis();
            String[] strings = Tools.splitFileName(fileName);
            fileName = strings[0] + "_" + l + "." + strings[1];
        }
        // 减少用户空间
        UserEntity userEntity = userDao.selectById(fileEntity.getUserId());
        userEntity.setUseSpace(userEntity.getUseSpace()-fileEntity.getFileSize());
        userDao.updateById(userEntity);
        fileEntity.setFileName(fileName);
        fileDao.updateById(fileEntity);
    }

    /**
     * 递归删除文件（真删除）
     * @param fileEntity
     */
    private void deleteFileRecursive(FileEntity fileEntity) {
        fileEntity.setIsValid(true);
        fileEntity.setFolderId(Constant.TEMP_FOLDER);
        fileEntity.setUserId(Constant.TEMP_USER);
        fileDao.updateById(fileEntity);

        Integer currentUserId = CurrentUser.getCurrentUserId();
        UserEntity userEntity = userDao.selectById(currentUserId);

        if(fileEntity.getFileSize() != null){
            userEntity.setUseSpace(userEntity.getUseSpace() - fileEntity.getFileSize());
        }

        userDao.updateById(userEntity);
    }


    @Override
    public void fileView(Integer fileId, HttpServletResponse response) {
        FileEntity fileEntity = fileDao.selectById(fileId);
        if(fileEntity==null){
            throw new CommonException(Constant.FILE_NOT_FOUND);
        }
        try {
            BufferedOutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            URL url = new URL(fileEntity.getFilePath());
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream());
            int i = 0;
            byte[] bytes = new byte[1024*10];
            while ((i = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, i);
            }
            outputStream.flush();
            outputStream.close();
            inputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}